from collections import Sequence
from google.appengine.api import memcache

class FetcherBase(Sequence):
    '''Used to paginate the results of queries.
    An instance of fetcher should not be an attribute of
    more than one class.'''
    def __init__(self, name, chunk_size, query_factory, length=None):
        self.mem_namespace = self.mem_namespace + name
        self.query_factory = query_factory
        if length: 
            self.length = length
        self.chunk_size = chunk_size
        
    def _get(self, pos):
        return memcache.get(str(pos), namespace=self.mem_namespace)
    
    def _set(self, pos, serialized):
        memcache.set(str(pos), value=serialized, namespace=self.mem_namespace)
        
    def _fetch(self, limit, offset):
        if not limit:
            return ()
        
        query = self.query_factory()
        
        pos = offset
        for pos in xrange(offset, 0, -limit):
            last_cursor = self._get(pos)
            if last_cursor is not None:
                query.with_cursor(last_cursor)
                break
        
        for pos in xrange(pos, offset, limit):
            if query.fetch(limit):
                next_cursor = query.cursor()
                query.with_cursor(next_cursor)
                self._set(pos + limit, next_cursor)
            else:
                return ()
            
        results = query.fetch(limit)
        if results:
            next_cursor = query.cursor()
            self._set(offset + limit, next_cursor)
        return results
    
    def __len__(self):
        l = self.length()
        s = self.chunk_size
        return -(-l//s)*s
    
    def __getitem__(self, s):
        if isinstance(s, slice):
            return self._fetch(s.stop - s.start, s.start)
        else:
            raise NotImplementedError
            #return self._fetch(1, s)[0]


def make_fetcher(model, name_suffix='_fetcher'):
    name = model.__name__ + name_suffix
    bases = (FetcherBase,)
    dict_ = {'mem_namespace': model.__module__ + '.' + model.__name__ + '.',
             'length': model.count}
    return type(name, bases, dict_)

def set_fetchers(model, chunk_sizes, dict_):
    fetcher = make_fetcher(model)
    fetcher_dict = {}
    for key, value in dict_.iteritems():
        for size in chunk_sizes: # make the length of a fetcher always be a multiple of chunk_size
            fetcher_dict[(key, size)] = fetcher(key, size, value)
    
    @classmethod
    def fetcher(cls, name, chunk_size):
        return fetcher_dict[(name, chunk_size)]
    
    model.fetcher = fetcher
            